<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CodeMirror Editor</title>
    <link rel="stylesheet" href="./lib/codemirror.css">
    <link rel="stylesheet" href="./theme/monokai.css">
  <link rel="stylesheet" href="./addon/fold/foldgutter.css" />
    <style>
		html, body {
            height: 100%;
            margin: 0;
			font-size:13px;
        }
        .CodeMirror {
            height: 100%;
        }
		.CodeMirror-foldmarker{
			text-shadow:none;
			color:#fff;
		}
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #000;
            border-radius: 8px;
        }
        ::-webkit-scrollbar-thumb {
            background: #999;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        body {
            overflow: auto;
            margin: 0;
        }
    </style>
</head>
<body>
<textarea id="code-json" name="code-json"></textarea>
<!--<Button style="position:absolute;z-index:999;bottom:0;left:0"
onclick="formatXml()">测试</Button>-->
<script src="./lib/codemirror.js"></script>

<script src="./addon/fold/foldcode.js"></script>
<script src="./addon/fold/foldgutter.js"></script>
<script src="./addon/fold/brace-fold.js"></script>
<script src="./addon/fold/xml-fold.js"></script>
<script src="./addon/fold/indent-fold.js"></script>
<script src="./addon/fold/markdown-fold.js"></script>
<script src="./addon/fold/comment-fold.js"></script>

<script src="./mode/javascript/javascript.js"></script>

<script src="./mode/xml/xml.js"></script>
<script src="./mode/css/css.js"></script>
<script src="./mode/htmlmixed/htmlmixed.js"></script>
<script src="./mode/python/python.js"></script>
<script src="./mode/markdown/markdown.js"></script>
<script>
	var te_json = document.getElementById("code-json");
	window.editor_json = CodeMirror.fromTextArea(te_json, {
	theme: 'monokai',
    mode: {name: "application/json", json: true},
    scrollbarStyle:'native',
	styleActiveLine: true,
	matchBrackets: true,
    lineNumbers: true,
    lineWrapping: true,
    extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
    foldGutter: true,
    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
    foldOptions: {
      widget: (from, to) => {
        var count = undefined;

        // Get open / close token
        var startToken = '{', endToken = '}';        
        var prevLine = window.editor_json.getLine(from.line);
        if (prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{')) {
          startToken = '[', endToken = ']';
        }

        // Get json content
        var internal = window.editor_json.getRange(from, to);
        var toParse = startToken + internal + endToken;

        // Get key count
        try {
          var parsed = JSON.parse(toParse);
          count = Object.keys(parsed).length;
        } catch(e) { }        

        return count ? `\u21A4${count}\u21A6` : '\u2194';
      }
    }
  });

    function formatJson() {
        var json = editor_json.getValue();
        if (json != ''&& json != null && json.trim() != '') {
            try {
                var parsed = JSON.parse(json);
                var formatted = JSON.stringify(parsed, null, 2); // 格式化为2个空格的缩进
                editor_json.setValue(formatted);
            } catch (e) {
                //alert('Invalid JSON');
            }
        }
    }

    function formatXml() {
        try {
            var xmlString = editor_json.getValue();
            let xmlStringFormatted = toformateXml(xmlString);
            editor_json.setValue(xmlStringFormatted);
        } catch (e) {
            console.log(e)
        }
    }

    function getValue() {
        return editor_json.getValue();
    }
    function setValue(jsonStr) {
        return editor_json.setValue(jsonStr);
    }
  
  
	//格式化xml代码
    function toformateXml(xmlStr){
        text = xmlStr;
        //使用replace去空格
        text = '\n' + text.replace(/(<\w+)(\s.*?>)/g,function($0, name, props){
            return name + ' ' + props.replace(/\s+(\w+=)/g," $1");
        }).replace(/>\s*?</g,">\n<");
        //处理注释
        text = text.replace(/\n/g,'\r').replace(/<!--(.+?)-->/g,function($0, text){
            var ret = '<!--' + escape(text) + '-->';
            return ret;
        }).replace(/\r/g,'\n');
        //调整格式  以压栈方式递归调整缩进
        var rgx = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/mg;
        var nodeStack = [];
        var output = text.replace(rgx,function($0,all,name,isBegin,isCloseFull1,isCloseFull2 ,isFull1,isFull2){
            var isClosed = (isCloseFull1 == '/') || (isCloseFull2 == '/' ) || (isFull1 == '/') || (isFull2 == '/');
            var prefix = '';
            if(isBegin == '!'){//!开头
                prefix = setPrefix(nodeStack.length);
            }else {
                if(isBegin != '/'){///开头
                    prefix = setPrefix(nodeStack.length);
                    if(!isClosed){//非关闭标签
                        nodeStack.push(name);
                    }
                }else{
                    nodeStack.pop();//弹栈
                    prefix = setPrefix(nodeStack.length);
                }
            }
            var ret =  '\n' + prefix + all;
            return ret;
        });
        var prefixSpace = -1;
        var outputText = output.substring(1);
        //还原注释内容
        outputText = outputText.replace(/\n/g,'\r').replace(/(\s*)<!--(.+?)-->/g,function($0, prefix,  text){
            if(prefix.charAt(0) == '\r')
                prefix = prefix.substring(1);
            text = unescape(text).replace(/\r/g,'\n');
            var ret = '\n' + prefix + '<!--' + text.replace(/^\s*/mg, prefix ) + '-->';
            return ret;
        });
        outputText= outputText.replace(/\s+$/g,'').replace(/\r/g,'\r\n');
        return outputText;
    }
     
    //计算头函数 用来缩进
    function setPrefix(prefixIndex) {
        var result = '';
        var span = '    ';//缩进长度
        var output = [];
        for(var i = 0 ; i < prefixIndex; ++i){
            output.push(span);
        }
        result = output.join('');
        return result;
    }
  
</script>
</body>
</html>